/**
 * HTMLEntityDecoder.java
 * 
 * FreeZzaph is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * FreeZzaph is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.
 */
package freezzaph.plugins;

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utility class for decoding entities in HTML.
 * Uses the list of entities from
 * http://www.w3.org/TR/html401/sgml/entities.html
 * to convert.
 * 
 * @author FreeZzaph
 */
public final class HTMLEntityDecoder {
	
	private static final Pattern dEntity = Pattern.compile("&#(\\d+);");
	private static final Pattern xEntity = Pattern.compile("&#x([a-f0-9]+);", Pattern.CASE_INSENSITIVE);
	private static final Pattern nEntity = Pattern.compile("&(\\w+);", Pattern.CASE_INSENSITIVE);

	private static final HashMap<String, Character> characterMap = new HashMap<String, Character>();
	
	// Initialize characterMap
	static {
		characterMap.put("nbsp", (char)160);
		characterMap.put("iexcl", (char)161);
		characterMap.put("cent", (char)162);
		characterMap.put("pound", (char)163);
		characterMap.put("curren", (char)164);
		characterMap.put("yen", (char)165);
		characterMap.put("brvbar", (char)166);
		characterMap.put("sect", (char)167);
		characterMap.put("uml", (char)168);
		characterMap.put("copy", (char)169);
		characterMap.put("ordf", (char)170);
		characterMap.put("laquo", (char)171);
		characterMap.put("not", (char)172);
		characterMap.put("shy", (char)173);
		characterMap.put("reg", (char)174);
		characterMap.put("macr", (char)175);
		characterMap.put("deg", (char)176);
		characterMap.put("plusmn", (char)177);
		characterMap.put("sup2", (char)178);
		characterMap.put("sup3", (char)179);
		characterMap.put("acute", (char)180);
		characterMap.put("micro", (char)181);
		characterMap.put("para", (char)182);
		characterMap.put("middot", (char)183);
		characterMap.put("cedil", (char)184);
		characterMap.put("sup1", (char)185);
		characterMap.put("ordm", (char)186);
		characterMap.put("raquo", (char)187);
		characterMap.put("frac14", (char)188);
		characterMap.put("frac12", (char)189);
		characterMap.put("frac34", (char)190);
		characterMap.put("iquest", (char)191);
		characterMap.put("Agrave", (char)192);
		characterMap.put("Aacute", (char)193);
		characterMap.put("Acirc", (char)194);
		characterMap.put("Atilde", (char)195);
		characterMap.put("Auml", (char)196);
		characterMap.put("Aring", (char)197);
		characterMap.put("AElig", (char)198);
		characterMap.put("Ccedil", (char)199);
		characterMap.put("Egrave", (char)200);
		characterMap.put("Eacute", (char)201);
		characterMap.put("Ecirc", (char)202);
		characterMap.put("Euml", (char)203);
		characterMap.put("Igrave", (char)204);
		characterMap.put("Iacute", (char)205);
		characterMap.put("Icirc", (char)206);
		characterMap.put("Iuml", (char)207);
		characterMap.put("ETH", (char)208);
		characterMap.put("Ntilde", (char)209);
		characterMap.put("Ograve", (char)210);
		characterMap.put("Oacute", (char)211);
		characterMap.put("Ocirc", (char)212);
		characterMap.put("Otilde", (char)213);
		characterMap.put("Ouml", (char)214);
		characterMap.put("times", (char)215);
		characterMap.put("Oslash", (char)216);
		characterMap.put("Ugrave", (char)217);
		characterMap.put("Uacute", (char)218);
		characterMap.put("Ucirc", (char)219);
		characterMap.put("Uuml", (char)220);
		characterMap.put("Yacute", (char)221);
		characterMap.put("THORN", (char)222);
		characterMap.put("szlig", (char)223);
		characterMap.put("agrave", (char)224);
		characterMap.put("aacute", (char)225);
		characterMap.put("acirc", (char)226);
		characterMap.put("atilde", (char)227);
		characterMap.put("auml", (char)228);
		characterMap.put("aring", (char)229);
		characterMap.put("aelig", (char)230);
		characterMap.put("ccedil", (char)231);
		characterMap.put("egrave", (char)232);
		characterMap.put("eacute", (char)233);
		characterMap.put("ecirc", (char)234);
		characterMap.put("euml", (char)235);
		characterMap.put("igrave", (char)236);
		characterMap.put("iacute", (char)237);
		characterMap.put("icirc", (char)238);
		characterMap.put("iuml", (char)239);
		characterMap.put("eth", (char)240);
		characterMap.put("ntilde", (char)241);
		characterMap.put("ograve", (char)242);
		characterMap.put("oacute", (char)243);
		characterMap.put("ocirc", (char)244);
		characterMap.put("otilde", (char)245);
		characterMap.put("ouml", (char)246);
		characterMap.put("divide", (char)247);
		characterMap.put("oslash", (char)248);
		characterMap.put("ugrave", (char)249);
		characterMap.put("uacute", (char)250);
		characterMap.put("ucirc", (char)251);
		characterMap.put("uuml", (char)252);
		characterMap.put("yacute", (char)253);
		characterMap.put("thorn", (char)254);
		characterMap.put("yuml", (char)255);
		characterMap.put("fnof", (char)402);
		characterMap.put("Alpha", (char)913);
		characterMap.put("Beta", (char)914);
		characterMap.put("Gamma", (char)915);
		characterMap.put("Delta", (char)916);
		characterMap.put("Epsilon", (char)917);
		characterMap.put("Zeta", (char)918);
		characterMap.put("Eta", (char)919);
		characterMap.put("Theta", (char)920);
		characterMap.put("Iota", (char)921);
		characterMap.put("Kappa", (char)922);
		characterMap.put("Lambda", (char)923);
		characterMap.put("Mu", (char)924);
		characterMap.put("Nu", (char)925);
		characterMap.put("Xi", (char)926);
		characterMap.put("Omicron", (char)927);
		characterMap.put("Pi", (char)928);
		characterMap.put("Rho", (char)929);
		characterMap.put("Sigma", (char)931);
		characterMap.put("Tau", (char)932);
		characterMap.put("Upsilon", (char)933);
		characterMap.put("Phi", (char)934);
		characterMap.put("Chi", (char)935);
		characterMap.put("Psi", (char)936);
		characterMap.put("Omega", (char)937);
		characterMap.put("alpha", (char)945);
		characterMap.put("beta", (char)946);
		characterMap.put("gamma", (char)947);
		characterMap.put("delta", (char)948);
		characterMap.put("epsilon", (char)949);
		characterMap.put("zeta", (char)950);
		characterMap.put("eta", (char)951);
		characterMap.put("theta", (char)952);
		characterMap.put("iota", (char)953);
		characterMap.put("kappa", (char)954);
		characterMap.put("lambda", (char)955);
		characterMap.put("mu", (char)956);
		characterMap.put("nu", (char)957);
		characterMap.put("xi", (char)958);
		characterMap.put("omicron", (char)959);
		characterMap.put("pi", (char)960);
		characterMap.put("rho", (char)961);
		characterMap.put("sigmaf", (char)962);
		characterMap.put("sigma", (char)963);
		characterMap.put("tau", (char)964);
		characterMap.put("upsilon", (char)965);
		characterMap.put("phi", (char)966);
		characterMap.put("chi", (char)967);
		characterMap.put("psi", (char)968);
		characterMap.put("omega", (char)969);
		characterMap.put("thetasym", (char)977);
		characterMap.put("upsih", (char)978);
		characterMap.put("piv", (char)982);
		characterMap.put("bull", (char)8226);
		characterMap.put("hellip", (char)8230);
		characterMap.put("prime", (char)8242);
		characterMap.put("Prime", (char)8243);
		characterMap.put("oline", (char)8254);
		characterMap.put("frasl", (char)8260);
		characterMap.put("weierp", (char)8472);
		characterMap.put("image", (char)8465);
		characterMap.put("real", (char)8476);
		characterMap.put("trade", (char)8482);
		characterMap.put("alefsym", (char)8501);
		characterMap.put("larr", (char)8592);
		characterMap.put("uarr", (char)8593);
		characterMap.put("rarr", (char)8594);
		characterMap.put("darr", (char)8595);
		characterMap.put("harr", (char)8596);
		characterMap.put("crarr", (char)8629);
		characterMap.put("lArr", (char)8656);
		characterMap.put("uArr", (char)8657);
		characterMap.put("rArr", (char)8658);
		characterMap.put("dArr", (char)8659);
		characterMap.put("hArr", (char)8660);
		characterMap.put("forall", (char)8704);
		characterMap.put("part", (char)8706);
		characterMap.put("exist", (char)8707);
		characterMap.put("empty", (char)8709);
		characterMap.put("nabla", (char)8711);
		characterMap.put("isin", (char)8712);
		characterMap.put("notin", (char)8713);
		characterMap.put("ni", (char)8715);
		characterMap.put("prod", (char)8719);
		characterMap.put("sum", (char)8721);
		characterMap.put("minus", (char)8722);
		characterMap.put("lowast", (char)8727);
		characterMap.put("radic", (char)8730);
		characterMap.put("prop", (char)8733);
		characterMap.put("infin", (char)8734);
		characterMap.put("ang", (char)8736);
		characterMap.put("and", (char)8743);
		characterMap.put("or", (char)8744);
		characterMap.put("cap", (char)8745);
		characterMap.put("cup", (char)8746);
		characterMap.put("int", (char)8747);
		characterMap.put("there4", (char)8756);
		characterMap.put("sim", (char)8764);
		characterMap.put("cong", (char)8773);
		characterMap.put("asymp", (char)8776);
		characterMap.put("ne", (char)8800);
		characterMap.put("equiv", (char)8801);
		characterMap.put("le", (char)8804);
		characterMap.put("ge", (char)8805);
		characterMap.put("sub", (char)8834);
		characterMap.put("sup", (char)8835);
		characterMap.put("nsub", (char)8836);
		characterMap.put("sube", (char)8838);
		characterMap.put("supe", (char)8839);
		characterMap.put("oplus", (char)8853);
		characterMap.put("otimes", (char)8855);
		characterMap.put("perp", (char)8869);
		characterMap.put("sdot", (char)8901);
		characterMap.put("lceil", (char)8968);
		characterMap.put("rceil", (char)8969);
		characterMap.put("lfloor", (char)8970);
		characterMap.put("rfloor", (char)8971);
		characterMap.put("lang", (char)9001);
		characterMap.put("rang", (char)9002);
		characterMap.put("loz", (char)9674);
		characterMap.put("spades", (char)9824);
		characterMap.put("clubs", (char)9827);
		characterMap.put("hearts", (char)9829);
		characterMap.put("diams", (char)9830);
		characterMap.put("quot", (char)34);
		characterMap.put("amp", (char)38);
		characterMap.put("lt", (char)60);
		characterMap.put("gt", (char)62);
		characterMap.put("OElig", (char)338);
		characterMap.put("oelig", (char)339);
		characterMap.put("Scaron", (char)352);
		characterMap.put("scaron", (char)353);
		characterMap.put("Yuml", (char)376);
		characterMap.put("circ", (char)710);
		characterMap.put("tilde", (char)732);
		characterMap.put("ensp", (char)8194);
		characterMap.put("emsp", (char)8195);
		characterMap.put("thinsp", (char)8201);
		characterMap.put("zwnj", (char)8204);
		characterMap.put("zwj", (char)8205);
		characterMap.put("lrm", (char)8206);
		characterMap.put("rlm", (char)8207);
		characterMap.put("ndash", (char)8211);
		characterMap.put("mdash", (char)8212);
		characterMap.put("lsquo", (char)8216);
		characterMap.put("rsquo", (char)8217);
		characterMap.put("sbquo", (char)8218);
		characterMap.put("ldquo", (char)8220);
		characterMap.put("rdquo", (char)8221);
		characterMap.put("bdquo", (char)8222);
		characterMap.put("dagger", (char)8224);
		characterMap.put("Dagger", (char)8225);
		characterMap.put("permil", (char)8240);
		characterMap.put("lsaquo", (char)8249);
		characterMap.put("rsaquo", (char)8250);
		characterMap.put("euro", (char)8364);
	}

	/**
	 * Decodes HTML entities to their character
	 * equivalents. Handles both named and
	 * numerical entities. Any unrecognized
	 * named entities will be replaced with
	 * three question marks ("???")
	 * 
	 * @param input the string potentially
	 * containing HTML entities.
	 * @return the same as the input string
	 * with the HTML entities replaced with
	 * their character equivalents.
	 */
	public static String decode(String input) {
		String output = input;
		
		// First, decode names entities
		Matcher m = nEntity.matcher(output);
		while (m.find()) {
			String entity = m.group(1);
			Character entityChar = characterMap.get(entity);
			if (entityChar != null) {
				output = output.replaceAll(m.group(0), entityChar.toString());
			} else {
				output = output.replaceAll(m.group(0), "???");
			}
			m = nEntity.matcher(output);
		}
		
		// Then decimal entities
		m = dEntity.matcher(output);
		while (m.find()) {
			output = output.replaceAll(m.group(0), (new Character((char)Integer.parseInt(m.group(1))).toString()).toString());
			m = nEntity.matcher(output);
		}
		
		// Then sexadecimal entities
		m = xEntity.matcher(output);
		while (m.find()) {
			output = output.replaceAll(m.group(0), (new Character((char)Integer.parseInt(m.group(1), 16)).toString()).toString());
			m = nEntity.matcher(output);
		}
		
		return output;
		
	}
}
